<?php

App::import('Controller', 'Tasks');
App::uses('CakeRequest', 'Network');
App::uses('CakeResponse', 'Network');
App::import('Component', 'Session');
App::uses('Task', 'Model');

/**
 * @author Omar Nada
 * The class contains test cases for the tasks controller.
 */
class TasksControllerTest extends ControllerTestCase {

    var $components = array('Auth', 'Cookie', 'Session', 'RequestHandler');

    /**
     * The function will be executed before running the test cases,
     *  it sets up the mocked objects for the test cases to use.
     * @author Omar Nada
     */
    function startTest() {
        $this->Tasks = new TasksControllerTest();
        $this->Tasks->constructClasses();
        $this->Tasks->initialize($this->Tasks);
        $this->TasksController = $this->generate('Tasks', array(
            'components' => array('Session', 'Auth' => array('User'), 'RequestHandler') //We mock the Auth Component here
                ));
        $this->Task = ClassRegistry::init('Task');
    }

    /**
     * the functiom tests the view all variables set by the controller.
     * @author Omar Nada
     * @access public 
     */
    public function testViewAllSimpleVars() {
        $this->TasksController->Auth->staticExpects($this->any())
                ->method('User', 'User')
                ->with($this->logicalOr(
                                $this->equalTo('userID'), $this->equalTo('accID'), $this->equalTo('admin')
                        ))
                ->will($this->returnCallback(array($this, 'viewAllSimpleVarsCallback')));
        $result = $this->testAction('/tasks/viewAll/', array('method' => 'get', 'return' => 'vars'));
        $expectedTaskString = 'tenth task';
        // first task returned is the tenth task
        $this->assertEquals($expectedTaskString, $result['tasks'][0]['tasks']['originalMessage']);
        // only two tasks are assigned.
        $this->assertEquals(sizeof($result['tasks']), 2);
    }

    /**
     *
     * @param int $foo
     * @return int
     * the function is called back whenever the User method in the Auth component is called.
     * it takes the parameter used by the Auth->User function, and returns an integer (used to
     * stub the function)
     * @author Omar Nada
     * @access public 
     */
    public function deletedAccountCallBack($foo) {
        if ($foo == 'userID')
            return 12;
        if ($foo == 'accID')
            return 1;
        return 0;
    }

    /**
     * The function tests the HTML view of the view all page.
     * It checks if some border colors exists, indicating that the tasks are shown.
     * @author Omar Nada
     *
     */
    public function testViewAllSimpleView() {
        $this->TasksController->Auth->staticExpects($this->any())
                ->method('User', 'User')
                ->with($this->logicalOr(
                                $this->equalTo('userID'), $this->equalTo('accID'), $this->equalTo('admin')
                        ))
                ->will($this->returnCallback(array($this, 'viewAllSimpleVarsCallback')));
        $result = $this->testAction('/tasks/viewAll/', array('method' => 'get', 'return' => 'view'));
        $this->assertContains('BORDERCOLOR=orange', $result);
        // no other assigned tasks are rejected, pending completed, or reassigned.
        $this->assertFalse(strpos($result, 'BORDERCOLOR=red') > 0);
        $this->assertFalse(strpos($result, 'BORDERCOLOR=green') > 0);
        $this->assertFalse(strpos($result, 'BORDERCOLOR=purple') > 0);
        $this->assertFalse(strpos($result, 'BORDERCOLOR=#659EC7') < 0);
    }

    /**
     * The function tests whether the user can access the view all page without an account ID
     * @author Omar Nada
     */
    public function testNullAccountID() {
        $this->TasksController->Auth->staticExpects($this->any())
                ->method('User', 'User')
                ->with($this->logicalOr(
                                $this->equalTo('userID'), $this->equalTo('accID'), $this->equalTo('admin')
                        ))
                ->will($this->returnCallback(array($this, 'nullAccountIDCallBack')));
        $result = $this->testAction('/tasks/viewAll/', array('method' => 'get', 'return' => 'view'));
        $this->assertTrue($result == null);
    }

    /**
     * the function checks if a deleted user can access the view users page.
     * @author Omar Nada
     */
    public function testDeletedUserPermission() {
        $this->TasksController->Auth->staticExpects($this->any())
                ->method('User', 'User')
                ->with($this->logicalOr(
                                $this->equalTo('userID'), $this->equalTo('accID'), $this->equalTo('admin')
                        ))
                ->will($this->returnCallback(array($this, 'deletedAccountCallBack')));
        $result = $this->testAction('/tasks/viewUsers/', array('method' => 'get', 'return' => 'view'));
        $this->assertFlase(isset($result));
    }

    /**
     * the function checks if a user is allowed to reassign task with no users 
     * remaining to reassign to
     * @author Omar Nada
     */
    public function testReassignFullTask() {
        $this->TasksController->Auth->staticExpects($this->any())
                ->method('User', 'User')
                ->with($this->logicalOr(
                                $this->equalTo('userID'), $this->equalTo('accID'), $this->equalTo('admin')
                        ))
                ->will($this->returnCallback(array($this, 'viewAllSimpleVarsCallback')));
        $result = $this->testAction('/tasks/reassign/10', array('method' => 'get', 'return' => 'view'));
        $this->assertTrue($result == null);
    }

    /**
     * the function tests if a user has access to the view all page
     * without enough permissions
     * @author Omar Nada
     */
    public function testInvalidPermission() {

        $this->TasksController->Auth->staticExpects($this->any())
                ->method('User', 'User')
                ->with($this->logicalOr(
                                $this->equalTo('userID'), $this->equalTo('accID'), $this->equalTo('admin')
                        ))
                ->will($this->returnCallback(array($this, 'invalidPermissionCallBack')));
        $result = $this->testAction('/tasks/viewAll/', array('method' => 'get', 'return' => 'view'));
        $this->assertTrue($result == null);
    }

    /**
     *
     * @param int $foo
     * @return int
     * the function is called back whenever $this->Auth->User is called in the testInvalidPermission, 
     * used to stub out the User method on the mocked Auth component..
     * @author Omar Nada 
     */
    public function invalidPermissionCallBack($foo) {
        if ($foo == 'accID')
            return 1;
        if ($foo == 'userID')
            return 8;
        return 0;
    }

    /**
     *
     * @param int $foo
     * @return int
     * the function is called back whenever $this->Auth->User is called in the testNullAccount, 
     * used to stub out the User method on the mocked Auth component..
     * @author Omar Nada 
     */
    public function nullAccountIDCallBack($foo) {
        if ($foo == 'accID')
            return null;
        if ($foo == 'userID')
            return 6;
        return 0;
    }

    /**
     *  
     *
     * @param int $foo
     * @return int
     * the function is called back whenever $this->Auth->User is called in the testViewAllSimpleVars, 
     * used to stub out the User method on the mocked Auth component..
     * @author Omar Nada 
     */
    public function viewAllSimpleVarsCallback($foo) {
        if ($foo == 'accID')
            return 1;
        if ($foo == 'userID')
            return 6;
        return 0;
    }

    /**
     * The function tests the reassign view, it checks whether
     *  the view contains checkboxes and submit button
     * @author Omar Nada
     */
    public function testReassignView() {
        $this->TasksController->Auth->staticExpects($this->any())
                ->method('User', 'User')
                ->with($this->logicalOr(
                                $this->equalTo('userID'), $this->equalTo('accID'), $this->equalTo('admin')
                        ))
                ->will($this->returnCallback(array($this, 'reassignCallBack')));
        $result = $this->testAction('/tasks/reassign/6', array('method' => 'get', 'return' => 'view'));
        // the submit button is created
        $this->assertContains("<form action=\"/cakephp/tasks/reassign/6\" id=\"TaskReassignForm\" method=\"post", $result);
        // some checkboxes are created
        $this->assertContains("<div class=\"checkbox\"><input type=\"checkbox\"", $result);
    }

    /**
     * the function checks the variables set whenever the reassign page is accessed,
     * it checks for the value of the users.
     * @authot Omar Nada
     */
    public function testReassignVars() {
        $this->TasksController->Auth->staticExpects($this->any())
                ->method('User', 'User')
                ->with($this->logicalOr(
                                $this->equalTo('userID'), $this->equalTo('accID'), $this->equalTo('admin')
                        ))
                ->will($this->returnCallback(array($this, 'reassignCallBack')));
        $result = $this->testAction('/tasks/reassign/6', array('method' => 'get', 'return' => 'vars'));
        $this->assertTrue(sizeof($result['users']) == 3);
        $this->assertTrue($result['users'][0]['u1']['userID'] == 4);
        $this->assertTrue($result['users'][1]['u1']['userID'] == 5);
        $this->assertTrue($result['users'][2]['u1']['userID'] == 6);
    }

    /**
     * The function tests if the user can reassign a non existing task.
     * @author Omar Nada
     */
    public function testReassignInvalidTask() {

        $this->TasksController->Auth->staticExpects($this->any())
                ->method('User', 'User')
                ->with($this->logicalOr(
                                $this->equalTo('userID'), $this->equalTo('accID'), $this->equalTo('admin')
                        ))
                ->will($this->returnCallback(array($this, 'reassignCallBack')));
        $result = $this->testAction('/tasks/reassign/invalid', array('method' => 'get', 'return' => 'view'));
        $this->assertTrue($result == null);
    }

    /**
     * The function checks if the user is logged in to an account and tries to 
     * reassign a task that belongs to another account.
     * @author Omar Nada
     */
    public function testReassignAnotherAccountTask() {
        $this->TasksController->Auth->staticExpects($this->any())
                ->method('User', 'User')
                ->with($this->logicalOr(
                                $this->equalTo('userID'), $this->equalTo('accID'), $this->equalTo('admin')
                        ))
                ->will($this->returnCallback(array($this, 'reassignAccountCallBack')));
        $result = $this->testAction('/tasks/reassign/11', array('method' => 'get', 'return' => 'vars'));
        $this->assertTrue(isset($result));
    }

    /**
     * The function checks if the user can reassign a task that he was not 
     * assigned to in the first place
     * @author Omar Nada
     */
    public function testReassignNotEnrolled() {
        $this->TasksController->Auth->staticExpects($this->any())
                ->method('User', 'User')
                ->with($this->logicalOr(
                                $this->equalTo('userID'), $this->equalTo('accID'), $this->equalTo('admin')
                        ))
                ->will($this->returnCallback(array($this, 'reassignNotEnrolledCallBack')));
        $result = $this->testAction('/tasks/reassign/3', array('method' => 'get', 'return' => 'vars'));
        $this->assertTrue(isset($result));
    }

    /**
     * The function reassigns the task to some users, checks if the tasks were reassigned
     * and then restores the database to its initial state
     * @author Omar Nada
     */
    public function testReassignSuccessful() {
        $this->TasksController->Auth->staticExpects($this->any())
                ->method('User', 'User')
                ->with($this->logicalOr(
                                $this->equalTo('userID'), $this->equalTo('accID'), $this->equalTo('admin')
                        ))
                ->will($this->returnCallback(array($this, 'reassignSuccessfulCallBack')));
        $data = array('Task' => array('sentTo' => array(0 => 4, 1 => 5), 'action' => 'reassign'));
        $result = $this->testAction('/tasks/reassign/2', array('data' => $data), array('method' => 'get'));
        $this->assertTrue(($result == 'task reassigned'));
        // restore the data
        $this->Task->query("delete from tasks_has_users where tasks_has_users.taskID = 2 AND (tasks_has_users.userID = 4 or tasks_has_users.userID = 5)");
        $this->Task->query("update tasks_has_users set tasks_has_users.permissionLevel = 'assignee' where tasks_has_users.userID = 3 AND tasks_has_users.taskID = 2");
    }

    /**
     * The function checks if the logged in user tries to reassign the task
     * to invalid users.
     * @author Omar Nada
     */
    public function testReassignInvalid() {
        $this->TasksController->Auth->staticExpects($this->any())
                ->method('User', 'User')
                ->with($this->logicalOr(
                                $this->equalTo('userID'), $this->equalTo('accID'), $this->equalTo('admin')
                        ))
                ->will($this->returnCallback(array($this, 'reassignSuccessfulCallBack')));
        $data = array('Task' => array('sentTo' => array(0 => 4, 1 => 5), 'action' => 'reassign'));
        $result = $this->testAction('/tasks/reassign/1', array('data' => $data), array('method' => 'get'));
        $this->assertTrue(($result == 'users not valid'));
        // restore the data
    }

    /**
     * The function checks if the logged in user can reassign the task without selecting
     * Users
     * @author Omar Nada
     */
    public function testReassignWithoutUsers() {
        $this->TasksController->Auth->staticExpects($this->any())
                ->method('User', 'User')
                ->with($this->logicalOr(
                                $this->equalTo('userID'), $this->equalTo('accID'), $this->equalTo('admin')
                        ))
                ->will($this->returnCallback(array($this, 'reassignSuccessfulCallBack')));
        $data = array('Task' => array('sentTo' => array(), 'action' => 'reassign'));
        $result = $this->testAction('/tasks/reassign/1', array('data' => $data), array('method' => 'get'));
        ;
        $this->assertTrue($result == 'empty users');
        // restore the data
    }

    /**
     * The functions makes the logged in user accept a pending task, checks if the task
     * was accepted and then restores the database to its initial state
     * @author Omar Nada
     */
    public function testAcceptTask() {
        $this->TasksController->Auth->staticExpects($this->any())
                ->method('User', 'User')
                ->with($this->logicalOr(
                                $this->equalTo('userID'), $this->equalTo('accID'), $this->equalTo('admin')
                        ))
                ->will($this->returnCallback(array($this, 'acceptTaskCallBack')));
        $data = array('Task' => array('status' => 'accepted', 'action' => 'accept', 'taskID' => 1));
        $this->testAction('/tasks/viewAll/', array('data' => $data), array('method' => 'get'));

        // restore the data
        // get database changes
        $query = $this->Task->query("select * from tasks_has_users where tasks_has_users.taskID = 1 AND tasks_has_users.userID = 3");
        $this->assertTrue($query[0]['tasks_has_users']['status'] == 'accepted');
        // restore the database
        $this->Task->query("update tasks_has_users set tasks_has_users.status = 'pending' where tasks_has_users.taskID = 1 AND tasks_has_users.userID = 3");
    }

    /**
     * The function checks if the logged in user can reject a pending task, it rejects the task
     * and resets the database to its initial state.
     * @author Omar Nada
     */
    public function testRejectTask() {
        $this->TasksController->Auth->staticExpects($this->any())
                ->method('User', 'User')
                ->with($this->logicalOr(
                                $this->equalTo('userID'), $this->equalTo('accID'), $this->equalTo('admin')
                        ))
                ->will($this->returnCallback(array($this, 'rejectTaskCallBack')));
        $data = array('Task' => array('status' => 'rejected', 'action' => 'reject', 'taskID' => 3));
        $this->testAction('/tasks/viewAll/', array('data' => $data), array('method' => 'get'));

        // restore the data
        // get database changes
        $query = $this->Task->query("select * from tasks_has_users where tasks_has_users.taskID = 3 AND tasks_has_users.userID = 5");
        $this->assertTrue($query[0]['tasks_has_users']['status'] == 'rejected');
        // restor the database
        $this->Task->query("update tasks_has_users set tasks_has_users.status = 'pending' where tasks_has_users.taskID = 1 AND tasks_has_users.userID = 3");
    }

    /**
     * The function checks if the logged in user can end an accepted task, it rejects the task
     * and resets the database to its initial state.
     * @author Omar Nada
     */
    public function testEndTask() {
        $this->TasksController->Auth->staticExpects($this->any())
                ->method('User', 'User')
                ->with($this->logicalOr(
                                $this->equalTo('userID'), $this->equalTo('accID'), $this->equalTo('admin')
                        ))
                ->will($this->returnCallback(array($this, 'rejectTaskCallBack')));
        $data = array('Task' => array('action' => 'end', 'taskID' => 8));
        $this->testAction('/tasks/viewAll/', array('data' => $data), array('method' => 'get'));

        // restore the data
        // get database changes
        $query = $this->Task->query("select * from tasks where tasks.taskID = 8");
        $this->assertTrue($query[0]['tasks']['status'] == 'complete');
        // restor the database
        $this->Task->query("update tasks set tasks.status = 'incomplete' where tasks.taskID = 8");
    }

    /**
     * the function checks if the user can reassign a task
     * @author Omar Nada
     */
    public function testReassignViewAll() {
        $this->TasksController->Auth->staticExpects($this->any())
                ->method('User', 'User')
                ->with($this->logicalOr(
                                $this->equalTo('userID'), $this->equalTo('accID'), $this->equalTo('admin')
                        ))
                ->will($this->returnCallback(array($this, 'rejectTaskCallBack')));
        //$this->TasksController->staticExpects($this->once())->method('redirect');
        $data = array('Task' => array('action' => 'reassign', 'taskID' => 7));
        $result = $this->testAction('/tasks/viewAll/', array('data' => $data), array('method' => 'get'));
        $this->assertTrue($result == 'reassign');
    }

    /**
     *
     * @param int $foo
     * @return int
     * the function is called back whenever $this->Auth->User is called in the testAcceptTask, 
     * used to stub out the User method on the mocked Auth component..
     * @author Omar Nada 
     */
    public function acceptTaskCallBack($foo) {
        if ($foo == 'accID')
            return 1;
        if ($foo == 'userID')
            return 3;
        return 0;
    }

    /**
     *
     * @param int $foo
     * @return int
     * the function is called back whenever $this->Auth->User is called in the testRejectTask, 
     * used to stub out the User method on the mocked Auth component..
     * @author Omar Nada 
     */
    public function rejectTaskCallBack($foo) {
        if ($foo == 'accID')
            return 1;
        if ($foo == 'userID')
            return 5;
        return 0;
    }

    /**
     *
     * @param int $foo
     * @return int
     * the function is called back whenever $this->Auth->User is called in the testReassignSuccessful, 
     * used to stub out the User method on the mocked Auth component..
     * @author Omar Nada 
     */
    public function reassignSuccessfulCallBack($foo) {
        if ($foo == 'userID')
            return 3;
        if ($foo == 'accID')
            return 1;
        return 0;
    }

    /**
     *
     * @param int $foo
     * @return int
     * the function is called back whenever $this->Auth->User is called in the testReassignNotEnrolled, 
     * used to stub out the User method on the mocked Auth component..
     * @author Omar Nada 
     */
    public function reassignNotEnrolledCallBack($foo) {
        if ($foo == 'accID')
            return 1;
        if ($foo == 'userID')
            return 4;
        return 0;
    }

    /**
     *
     * @param int $foo
     * @return int
     * the function is called back whenever $this->Auth->User is called in the testReassign, 
     * used to stub out the User method on the mocked Auth component..
     * @author Omar Nada 
     */
    public function reassignCallBack($foo) {
        if ($foo == 'accID')
            return 1;
        if ($foo == 'userID')
            return 3;

        return 0;
    }

    /**
     *
     * @param int $foo
     * @return int
     * the function is called back whenever $this->Auth->User is called in the testReassignAccount, 
     * used to stub out the User method on the mocked Auth component..
     * @author Omar Nada 
     */
    public function reassignAccountCallBack($foo) {
        if ($foo == 'accID')
            return 1;
        if ($foo == 'userID')
            return 2;
        return 0;
    }

    /**
     * The function is called whenver the test cases finish running. it removes
     * all create objects
     */
    function endTest() {
        unset($this->Tasks);
        ClassRegistry::flush();
    }

}